package com.javabi.common.lang;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.lang.management.ThreadInfo;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Throwables;
import com.javabi.common.date.timer.ITimer;
import com.javabi.common.date.timer.NanoTimer;

public class VirtualMachine implements IVirtualMachine {

	private static final Logger log = LoggerFactory.getLogger(VirtualMachine.class);

	@Override
	public long getProcessId() {
		String name = ManagementFactory.getRuntimeMXBean().getName();
		int index = name.indexOf('@');
		return Long.parseLong(name.substring(0, index));
	}

	@Override
	public long getMaximumMemory() {
		return Java.getMaximumMemory();
	}

	@Override
	public long getCommittedMemory() {
		return Java.getCommittedMemory();
	}

	@Override
	public long getFreeMemory() {
		return Java.getFreeMemory();
	}

	@Override
	public long getUsedMemory() {
		return Java.getUsedMemory();
	}

	@Override
	public InetAddress getLocalHost() {
		try {
			return InetAddress.getLocalHost();
		} catch (Exception e) {
			throw Throwables.propagate(e);
		}
	}

	@Override
	public String getHostName() {
		return getLocalHost().getHostName();
	}

	@Override
	public String getHostAddress() {
		return getLocalHost().getHostAddress();
	}

	@Override
	public final String getOperatingSystem() {
		return System.getProperty("os.name");
	}

	@Override
	public final String getUserName() {
		return System.getProperty("user.name");
	}

	@Override
	public String getTimeZone() {
		return System.getProperty("user.timezone");
	}

	@Override
	public String getFileEncoding() {
		return System.getProperty("file.encoding");
	}

	@Override
	public String getFileSeparator() {
		return System.getProperty("file.separator");
	}

	@Override
	public String getPathSeparator() {
		return System.getProperty("path.separator");
	}

	@Override
	public String getLineSeparator() {
		return System.getProperty("line.separator");
	}

	@Override
	public String getClassPath() {
		return System.getProperty("java.class.path");
	}

	@Override
	public String getLibraryPath() {
		return System.getProperty("java.library.path");
	}

	@Override
	public List<String> getClassPathList() {
		String separator = getPathSeparator();
		return Arrays.asList(getClassPath().split(separator));
	}

	@Override
	public List<String> getLibraryPathList() {
		String separator = getPathSeparator();
		return Arrays.asList(getLibraryPath().split(separator));
	}

	@Override
	public List<String> getInputArguments() {
		return ManagementFactory.getRuntimeMXBean().getInputArguments();
	}

	@Override
	public long getStartTime() {
		return ManagementFactory.getRuntimeMXBean().getStartTime();
	}

	@Override
	public long getUptime() {
		return ManagementFactory.getRuntimeMXBean().getUptime();
	}

	@Override
	public int getThreadCount() {
		return getStackTraces().size();
	}

	@Override
	public int getPeakThreadCount() {
		return ManagementFactory.getThreadMXBean().getPeakThreadCount();
	}

	@Override
	public ThreadInfo getThreadInfo(long id) {
		return ManagementFactory.getThreadMXBean().getThreadInfo(id);
	}

	@Override
	public Set<Thread> getThreads() {
		return getStackTraces().keySet();
	}

	@Override
	public Map<Thread, StackTraceElement[]> getStackTraces() {
		return Thread.getAllStackTraces();
	}

	@Override
	public int getLoadedClassCount() {
		return ManagementFactory.getClassLoadingMXBean().getLoadedClassCount();
	}

	@Override
	public MemoryUsage getHeapMemoryUsage() {
		return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
	}

	@Override
	public MemoryUsage getNonHeapMemoryUsage() {
		return ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
	}

	@Override
	public Map<String, String> getEnvironmentProperties() {
		return System.getenv();
	}

	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Map<String, String> getSystemProperties() {
		return (Map) System.getProperties();
	}

	@Override
	public long getCurrentTime() {
		return Java.getCurrentTime();
	}

	@Override
	public long getNanoTime() {
		return Java.getNanoTime();
	}

	@Override
	public int getCpuCount() {
		return Runtime.getRuntime().availableProcessors();
	}

	@Override
	public int getCurrentTimeInSeconds() {
		return Java.getCurrentTimeInSeconds();
	}

	@Override
	public String getJavaVersion() {
		return System.getProperty("java.version");
	}

	@Override
	public String getWorkingDirectory() {
		return System.getProperty("user.dir");
	}

	@Override
	public void garbageCollect() {
		MemoryUsage heap = new VirtualMachine().getHeapMemoryUsage();
		Percent percent = new Percent(heap.getUsed(), heap.getMax(), 1);

		ITimer timer = new NanoTimer();
		timer.start();
		Runtime.getRuntime().gc();
		timer.stop();

		heap = new VirtualMachine().getHeapMemoryUsage();
		percent = new Percent(heap.getUsed(), heap.getMax(), 1);

		log.info("[Garbage Collection] Completed in " + timer + " (" + new ByteSize(heap.getUsed()) + ", " + percent + ")");
	}

	@Override
	public void exit(int status) {
		SystemExit.exit(status);
	}

}
